﻿using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Microsoft.Practices.Unity;
using NUnit.Framework;
using ReactiveGraph.Core;
using ReactiveGraph.Core.Configuration.Containers;
using ReactiveGraph.Core.Extensions;
using ReactiveGraph.Object.Configuration;
using ReactiveGraph.Object.Configuration.Containers;

namespace ReactiveGraph.Object.Tests
{
    [TestFixture]
    public class NodeCollectionTests
    {
        private UnityContainer _container;

        [SetUp]
        public void Setup()
        {
            _container = new UnityContainer();
            _container.AddExtension(new UnityObjectExtension());
            _container.RegisterType<Foo>();
            _container.RegisterType(typeof(ReactiveObjectCollection<>));
            _container.RegisterType<Bar>();
        }

        [Test]
        public void CanAdd()
        {
            
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); 
            collection.AddNew();
            collection.Count.Should().Be(1);
        }

        [Test]
        public void CanRemove()
        {

            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>();
            var node = _container.Resolve<Foo>();
            collection.Add(node);
            collection.Remove(node);
            collection.Count.Should().Be(0);
        }

        [Test]
        public void Contain()
        {
            
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); ;
            var node = _container.Resolve<Foo>();
            collection.Add(node);
            collection.Contains(node).Should().BeTrue();
        }

        [Test]
        public void Index()
        {
            
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); ;
            var node = _container.Resolve<Foo>();
            collection.Add(node);
            collection.IndexOf(node).Should().Be(0);
        }

    

        [Test]
        public void ChildAdded()
        {
            
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); ;
            var items = new List<Foo>();
            var behaviour = collection.ChildAdded().Do(items.Add).ToUnit();
            collection.AttachBehaviour(behaviour);
            collection.Behaviours.Subscribe();
            collection.AddNew("F1");
            items.Should().HaveCount(1);
        }

        [Test]
        public void ChildRemoved()
        {
            
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); ;
            var node = _container.Resolve<Foo>();
            collection.Add(node);
            var items = new List<Foo>();
            var behaviour = collection.ChildRemoved().Do(items.Add).ToUnit();
            collection.AttachBehaviour(behaviour);
            collection.Behaviours.Subscribe();
            collection.Remove(node);
            items.Should().HaveCount(1);
        }

        [Test]
        public void ChildPropertyChanged()
        {
            
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); ;
            var node = _container.Resolve<Foo>();
            collection.Add(node);
            var list = new List<IProperty>();
            collection.ChildPropertyChanged().Do(list.Add).Subscribe();
            node.Name.Value = "hah";
            list.Should().HaveCount(1);
        }

        [Test]
        public void DeepChildPropertyChanged()
        {
            
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); ;
            var node = _container.Resolve<Foo>();
            collection.Add(node);
            var list = new List<IProperty>();
            collection.DescendantPropertyChanged(foo => foo.Bar.Name).Do(list.Add).Subscribe();
            node.Name.Value = "hah";
            list.Should().HaveCount(0);
            node.Bar.Name.Value = "hah";
            list.Should().HaveCount(1);
        }

        [Test]
        public void SyncChildProperties()
        {
            var collection = _container.Resolve<ReactiveObjectCollection<Foo>>(); ;
            
            collection.AddNew("F1");
            collection.SyncChildProperties(foo => foo.Bar.Name).Subscribe();
            
            collection.AddNew("Foo1");
            collection.AddNew("Foo2");
            collection.First().Bar.Name.Value = "Hey";
            collection.Last().Bar.Name.Value.Should().Be("Hey");
            collection.AddNew("Foo3");
            collection.Last().Bar.Name.Value.Should().Be("Hey");



        }

        class Foo : ReactiveObject<Foo>
        {
            public Foo(Context context) 
                : base()
            {
            }

            public Bar Bar { get; set; }
            public new Property<string> Name { get; set; } 
        }

        class Bar : ReactiveObject<Bar>
        {
            public Bar(Context context)
                : base()
            {
            }

            public new Property<string> Name { get; set; }
        }
    }
}